﻿using CleanArchitecture.Core.Configuration;
using CleanArchitecture.Core.Handlers;
using CleanArchitecture.Core.Helper;
using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Infrastructure.Entities.Business;
using CleanArchitecture.Infrastructure.Entities.Settings;
using CleanArchitecture.Infrastructure.Interfaces;
using CleanArchitecture.Infrastructure.Interfaces.Business;
using CleanArchitecture.Infrastructure.Interfaces.SystemLog;
using CleanArchitecture.Infrastructure.Middleware;
using CleanArchitecture.Infrastructure.Services;
using CleanArchitecture.Infrastructure.Services.SignalR;
using Masuit.LuceneEFCore.SearchEngine;
using Masuit.LuceneEFCore.SearchEngine.Interfaces;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Net.Http.Headers;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace CleanArchitecture.Infrastructure
{
    /// <summary>
    /// Represents extensions of IApplicationBuilder
    /// </summary>
    public static class StartupConfigSetup
    {
        /// <summary>
        /// Configure the application HTTP request pipeline
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void ConfigureRequestPipeline(this IApplicationBuilder application)
        {
            EngineContext.Current.ConfigureRequestPipeline(application);
        }

        /// <summary>
        /// Add exception handling
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UseCustomExceptionHandler(this IApplicationBuilder application)
        {
            var nowaConfig = EngineContext.Current.Resolve<NowaConfig>();
            var hostingEnvironment = EngineContext.Current.Resolve<IWebHostEnvironment>();
            Func<IWebHostEnvironment, bool> IsDevEnv = env => env.EnvironmentName == "Development" ? true : false;
            //Func<bool> IsDevEnv = () => { return hostingEnvironment.EnvironmentName == "Development"; };
            var useDetailedExceptionPage = nowaConfig.DisplayFullErrorStack || IsDevEnv(hostingEnvironment);
            if (useDetailedExceptionPage)
            {
                //get detailed exceptions for developing and testing purposes
                application.UseDeveloperExceptionPage();
            }
            else
            {
                //or use special exception handler
                application.UseExceptionHandler(StartupConfig.DefaultRoutePath.ErrPage/*"/errorpage.htm"*/);
            }

            //log errors
            application.UseExceptionHandler(handler =>
            {
                handler.Run(context =>
                {
                    var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
                    if (exception == null)
                        return Task.CompletedTask;

                    try
                    {
                        //get current customer
                        //var currentCustomer = EngineContext.Current.Resolve<IWorkContext>().CurrentCustomer;
                        //log error
                        EngineContext.Current.Resolve<ISystemLogService>().Error("System Occured an Error ", exception);
                    }
                    finally
                    {
                        //rethrow the exception to show the error page
                        throw exception;
                    }
                });
            });
        }

        /// <summary>
        /// Adds a special handler that checks for responses with the 404 status code that do not have a body
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UsePageNotFound(this IApplicationBuilder application)
        {
            application.UseStatusCodePages(async context =>
            {
                var httpContext = context.HttpContext;
                //handle 404 Not Found
                if (httpContext.Response.StatusCode == StatusCodes.Status404NotFound)
                {
                    var webHelper = EngineContext.Current.Resolve<IWebHelper>();
                    if (!webHelper.IsStaticResource())
                    {
                        //get original path and query
                        var originalPath = httpContext.Request.Path;
                        var originalQueryString = httpContext.Request.QueryString;

                        //store the original paths in special feature, so we can use it later
                        httpContext.Features.Set<IStatusCodeReExecuteFeature>(new StatusCodeReExecuteFeature()
                        {
                            OriginalPathBase = httpContext.Request.PathBase.Value,
                            OriginalPath = originalPath.Value,
                            OriginalQueryString = originalQueryString.HasValue ? originalQueryString.Value : null,
                        });

                        //get new path
                        httpContext.Request.Path = StartupConfig.DefaultRoutePath.PageNotFound404;//"/page-not-found";
                        httpContext.Request.QueryString = QueryString.Empty;

                        try
                        {
                            //re-execute request with new path
                            await context.Next(context.HttpContext);
                        }
                        finally
                        {
                            //return original path to request
                            httpContext.Request.QueryString = originalQueryString;
                            httpContext.Request.Path = originalPath;
                            httpContext.Features.Set<IStatusCodeReExecuteFeature>(null);
                        }
                    }
                }
            });
        }

        /// <summary>
        /// Adds a special handler that checks for responses with the 400 status code (bad request)
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UseBadRequestResult(this IApplicationBuilder application)
        {
            //application.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
            application.UseStatusCodePages(context =>
            {
                //handle 400 (Bad request)
                if (context.HttpContext.Response.StatusCode == StatusCodes.Status400BadRequest)
                {
                    //var logger = EngineContext.Current.Resolve<ILogger>();
                    //var workContext = EngineContext.Current.Resolve<IWorkContext>();
                    //logger.Error("Error 400. Bad request", null, customer: workContext.CurrentCustomer);
                }

                return Task.CompletedTask;
            });
        }

        /// <summary>
        /// Configure static file serving
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UseCustomStaticFiles(this IApplicationBuilder application)
        {
            Action<StaticFileResponseContext> staticFileResponse = (context) =>
            {
                var commonSettings = EngineContext.Current.Resolve<CommonSettings>();
                if (!string.IsNullOrEmpty(commonSettings.StaticFilesCacheControl))
                context.Context.Response.Headers.Append(HeaderNames.CacheControl, commonSettings.StaticFilesCacheControl);
            };

            //common static files
            application.UseStaticFiles(new StaticFileOptions { OnPrepareResponse = staticFileResponse });
        }

        /// <summary>
        /// Configure middleware checking whether requested page is keep alive page
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UseKeepAlive(this IApplicationBuilder application)
        {
            application.UseMiddleware<KeepAliveMiddleware>();
        }

        /// <summary>
        /// Adds the authentication middleware, which enables authentication capabilities.
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UseCustomAuthentication(this IApplicationBuilder application)
        {
            application.UseMiddleware<AuthenticationMiddleware>();
        }

        /// <summary>
        /// Configure the request localization feature
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UseRequestLocalization(this IApplicationBuilder application)
        {
            application.UseRequestLocalization(options =>
            {
                //prepare supported cultures
                var cultures = EngineContext.Current.Resolve<ILanguageService>().GetLanguagesAll()
                    .OrderBy(language => language.DisplayOrder)
                    .Select(language => new CultureInfo(language.Culture)).ToList();
                options.SupportedCultures = cultures;
                options.DefaultRequestCulture = new RequestCulture(cultures.FirstOrDefault());
            });
        }

        /// <summary>
        /// Set current culture info
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UseCulture(this IApplicationBuilder application)
        {
            application.UseMiddleware<CultureMiddleware>();
        }
        /// <summary>
        /// Configure MVC routing
        /// DavidLee 2019/11/8 添加Mvc Route(路由)配置，集中配置Mvc访问路由
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UseCustomNetCoreMvc(this IApplicationBuilder application)
        {
            application.UseCors("CorsPolicy");
            application.UseMvc(routeBuilder =>
            {
                //register all routes
                EngineContext.Current.Resolve<IRoutePublisher>().RegisterRoutes(routeBuilder);
            });
        }
        /// <summary>
        /// Configure Endpoints routing
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public static void UseCustomEndpoints(this IApplicationBuilder application)
        {
            //Add the EndpointRoutingMiddleware
            application.UseRouting();

            //Execute the endpoint selected by the routing middleware
            application.UseEndpoints(endpoints =>
            {
                //register all routes
                EngineContext.Current.Resolve<IRoutePublisher>().RegisterEndpointRoutes(endpoints);
                endpoints.MapHub<NotifyHub>(StartupConfig.DefaultRoutePath.NotifyHubEndPoint);
            });
        }
        public static void UseLuceneSearchEngine(this IApplicationBuilder application)
        {
            // 初始化索引库，建议结合定时任务使用，定期刷新索引库
            var hostEnv = EngineContext.Current.Resolve<IWebHostEnvironment>();
            var luceneIndexerOptions = EngineContext.Current.Resolve<LuceneIndexerOptions>();
            var searchEngine = EngineContext.Current.Resolve<ISearchEngine<AppDbObjectContext>>();
            string lucenePath = CommonHelper.DefaultFileProvider.Combine(hostEnv.ContentRootPath, luceneIndexerOptions.Path);
            if (!Directory.Exists(lucenePath) || Directory.GetFiles(lucenePath).Length < 1)
            {
                Console.WriteLine("索引库不存在，开始自动创建Lucene索引库...");
                searchEngine.CreateIndex(new List<string>()
                {
                    nameof(NewsItem),
                });
                //var list = searchEngine.Context.Post.Where(i => i.Status != Status.Pended).ToList(); // 删除不需要被索引的数据
                //searchEngine.LuceneIndexer.Delete(list);
                Console.WriteLine("索引库创建完成！");
            }
        }
    }
}